﻿#include "DispatchCore.h"
#include "global.h"
#include "Monitor/PortStateMonitor.h"
/*
出入柜指令由上位机下发后，整套流程由接收来自门电路的串口数据处理函数slot_portMessageProcess中驱动。其中入柜（入库，归还）指令均有屏来分配新货位，出柜指令使用上位机下发的货位。
入柜指令先分配好相应规格的存储货位，然后分配好相应规格的出货口货位后，该指令变为挂起态等待用户将档案放入指定位置，用户放入档案后，该指令变为ready态。
出柜指令分配好相应规格的出货口货位后，该指令直接变为ready态。

在slot_portMessageProcess的每次循环中不断检测是否有ready态的指令，如果当前没有正在执行的指令，则按照循序依次执行ready态的指令。

出柜指令完成后，指令变为完成态，并不会直接消失，待用户从指令出货口位置拿走档案后再消失，该判断逻辑在slot_portMessageProcess中。
入柜指令完成后，指令直接消失，该判断逻辑在InOutCabCmdResultProcess中。

任何指令由下发到完成过程中出现任何问题，都变为dead态，并不会主动消失。
*/
DispatchCore::DispatchCore(QObject *parent) : QObject(parent)
{

}

DispatchCore::~DispatchCore()
{
    taskTimer->stop();

    // 正确关闭线程顺序
    pThread_PortStateMonitor->quit();
    pThread_PortStateMonitor->wait();
    pThread_PortStateMonitor->deleteLater();
    pPortStateMonitor->deleteLater();

    if(m_client != nullptr){
        m_client->deleteLater();
    }
    if(m_pWebSocketServer){
        m_pWebSocketServer->deleteLater();
    }
    if(pKincoServoManager != nullptr){
        delete pKincoServoManager;
        pKincoServoManager = nullptr;
    }
    if(pServerUdpMonitor != nullptr)
    {
        delete pServerUdpMonitor;
        pServerUdpMonitor = nullptr;
    }
}

void DispatchCore::slot_CoreInit()
{
    m_pWebSocketServer = new QWebSocketServer(QStringLiteral("Echo Server"), QWebSocketServer::NonSecureMode, this);

    if (m_pWebSocketServer->listen(QHostAddress::Any, 12345)) {
        connect(m_pWebSocketServer, SIGNAL(newConnection()), this, SLOT(slot_newWsConnection()));
        mylog << "WebSocket server started on port" << 12345;
    } else {
        mylog << "Failed to start WebSocket server:" << m_pWebSocketServer->errorString();
    }

    pKincoServoManager = new KincoServoManager(m_client);
    pServerUdpMonitor = new ServerUdpMonitor(QHostAddress("192.168.0.99"),22222);

    connect(pServerUdpMonitor,SIGNAL(sig_ServerCommandReady(QJsonObject)),this,SLOT(slot_ServerCommandAnalysis(QJsonObject)));
    QList<QVariantList> allrecord;
    //读取出货口货位设置规格，存入出货口状态指示列表中
    if(DeviceDBManager::getInstance()->mPortConfigDB.getAllRecord(allrecord))
    {
        for(int i=0;i<allrecord.size();i++)
        {
            PortGoodsIndicator port;
            port.standard = allrecord[i][1].toInt();
            mPortOccupy<<port;
        }
    }

    pPortStateMonitor = new PortStateMonitor();
    connect(pPortStateMonitor,SIGNAL(sig_portMessageProcess(QVector<bool>)),this,SLOT(slot_portMessageProcess(QVector<bool>)));
    //启动出口串口线程
    pThread_PortStateMonitor = new QThread(this);
    connect(pThread_PortStateMonitor,SIGNAL(started()),pPortStateMonitor,SLOT(slot_PortStateMonitorInit()));
    pPortStateMonitor->moveToThread(pThread_PortStateMonitor);
    pThread_PortStateMonitor->start();

    taskTimer = new QTimer();
    connect(taskTimer,SIGNAL(timeout()),this,SLOT(slot_taskTimerTimeout()));
    taskTimer->start(1000);
}
/***************************************************************
* 函数名称：distributePort(int standard, int &pos)
* 功能描述：根据出货口货位的信息状态分配一个未使用的出货口
* 参数说明：standard-规格 pos-出货口序号
* 返回值说明：是否分配成功
***************************************************************/
bool DispatchCore::distributePort(int standard, int &pos)
{
    bool ok = false;

    //遍历这个规格的出货口是否有没被占用的
    for(int i=0;i<10;i++){
        if(mPortOccupy[i].standard == standard && mPortOccupy[i].getOccupy() == false){
            pos = i;
            ok = true;
            break;
        }
    }

    return ok;
}
/***************************************************************
* 函数名称：QJson2StdString(QJsonObject json)
* 功能描述：QJson转std字符串
* 参数说明：
* 返回值说明：
***************************************************************/
string DispatchCore::QJson2StdString(QJsonObject json)
{
    // 将QJsonObject转化为std::string字符串
    QJsonDocument jsonDoc(json);
    return jsonDoc.toJson(QJsonDocument::Compact).toStdString();
}
/***************************************************************
* 函数名称：slot_recieveDataFromClient(QJsonObject data)
* 功能描述：接收来自Linux控制板的TCP信息并解析处理
* 参数说明：data-数据
* 返回值说明：
***************************************************************/
void DispatchCore::recieveDataFromClient(QJsonObject data)
{
    if(data.contains("CmdType"))
    {
        QString cmdtype = data.value("CmdType").toString();
        if(cmdtype == "SETPARAMS")
        {
            emit sig_setParamsResult(data);
        }
        else if(cmdtype == "PROBE")
        {
            emit sig_ParamsResult(data);
        }
        //处理来自Linux控制板的QR码定位结果
        else if(cmdtype == "QRLOCATE")
        {
            //转换成实体类
            string response = QJson2StdString(data);
            QRLocateCommandResponse qRLocateCommandResponse;
            JsonHelper::JsonToObject(qRLocateCommandResponse, response);
            //处理结果
            QRLocateResultProcess(qRLocateCommandResponse);
        }
        //处理来自Linux控制板的指令执行结果
        else if(cmdtype == "INOUTCABCMD")
        {
            //转换成实体类
            string response = QJson2StdString(data);
            InOutCabCommandResponse inOutCabCommandResponse;
            JsonHelper::JsonToObject(inOutCabCommandResponse, response);
            //处理结果
            InOutCabCmdResultProcess(inOutCabCommandResponse);
        }
    }
}
/***************************************************************
* 函数名称：slot_sendCmd2LinuxClient(QJsonObject json)
* 功能描述：发送数据给Linux控制板
* 参数说明：json-数据
* 返回值说明：
***************************************************************/
void DispatchCore::slot_sendCmd2LinuxClient(QJsonObject json)
{
    // 构建 JSON 文档
    QJsonDocument document;
    document.setObject(json);
    mylog<<json;
    QString byteArray = document.toJson(QJsonDocument::Compact);

    if(m_client)
        m_client->sendTextMessage(byteArray);
}


void DispatchCore::slot_setParams(int deviceno, int objectindex, int value)
{
    pKincoServoManager->SetParams(deviceno,objectindex,value);
}

void DispatchCore::slot_setControl(uint8_t deviceno, uint8_t value)
{
    pKincoServoManager->SetControl(deviceno,value);
}

void DispatchCore::slot_setProbe(quint8 deviceno, quint8 type)
{
    pKincoServoManager->SetProbe(deviceno,type);
}

void DispatchCore::slot_moveCmd(QString type,int value1,int value2,QString word)
{
    pKincoServoManager->SetMoveCmd(type,value1,value2,word);
}

void DispatchCore::slot_imgCmd()
{
    QJsonObject json;
    json.insert("CmdType", "IMGCMD");
    QJsonDocument document;
    document.setObject(json);
    mylog<<json;
    QString data = document.toJson(QJsonDocument::Compact);
    if(m_client)
         m_client->sendTextMessage(data);
}

void DispatchCore::slot_inOutCabCmd(InOutCabCommand_Board cmd)
{
    // 构建 JSON 对象
    QJsonObject json;
    json.insert("CmdType", "INOUTCABCMD");
    json.insert("PortLocation_X",cmd.PortLocation_X);
    json.insert("PortLocation_Y",cmd.PortLocation_Y);
    json.insert("StorgeLocation_X",cmd.StorgeLocation_X);
    json.insert("StorgeLocation_Y",cmd.StorgeLocation_Y);
    json.insert("Type",cmd.Type);
    json.insert("HaveNext",cmd.HaveNext);
    json.insert("Face",cmd.Face);
    json.insert("QRCode",cmd.QRCode);
    json.insert("CmdCode",cmd.CmdCode);
    json.insert("IsDebug",cmd.isDebug);
    json.insert("StepCnt",cmd.StepCnt);
    // 构建 JSON 文档
    QJsonDocument document;
    document.setObject(json);
    mylog<<json;
    QString byteArray = document.toJson(QJsonDocument::Compact);

    if(m_client)
        m_client->sendTextMessage(byteArray);
}

void DispatchCore::slot_sendQRLocateCmd(QRLocateCommand cmd)
{
    QJsonArray array;
    for(int i=0;i<cmd.subWidth.size();i++)
    {
        array.append(cmd.subWidth[i]);
    }
    QJsonObject json;
    json.insert("CmdType","QRLOCATE");
    json.insert("TotalLayer",cmd.totalLayer);
    json.insert("TotalSub",cmd.totalSub);
    json.insert("PortLayer",cmd.portLayer);
    json.insert("SubWidth",array);
    json.insert("Debug",cmd.isDebug);
    // 构建 JSON 文档
    QJsonDocument document;
    document.setObject(json);
    mylog<<json;
    QString byteArray = document.toJson(QJsonDocument::Compact);

    if(m_client)
        m_client->sendTextMessage(byteArray);
}
/***************************************************************
* 函数名称：slot_portMessageProcess(uint16_t portoccupy)
* 功能描述：处理来自出货口的货位状态信息,并为每个指令分配出货口同时下发指令给Linux控制板
* 参数说明：portoccupy-出货口实际状态 0-无物体 1-有物体
* 返回值说明：
***************************************************************/
void DispatchCore::slot_portMessageProcess(QVector<bool> portstatus)
{
    //更新门货位信息
    for(int i=0;i<5;++i){
        mylog<<"port "<<i<< "status:"<<portstatus[i];
        mPortOccupy[i].setPhysicalOccupy(portstatus[i]);
        if(portstatus[i]){
            //判断该货位是否被分配了指令，并且该指令是否为入柜，如果是则需要将该指令从挂起态变为ready态
            if(mPortOccupy[i].isCmdOcuupy() && !mPortOccupy[i].isOutCab())
            {
                //遍历指令列表查找占据该货位的指令。没有在货位信息中保存该指令的序号是因为中途有删除指令，那么该序号就会不对
                for(int j=0;j<mServerCommand.size();j++)
                {
                    if(mServerCommand[j].PortNo == i)
                        mServerCommand[j].CmdState = CmdState_Ready;
                }
            }
        }
        else{
            //判断该货位是否被分配了指令，并且该指令是出柜，如果是则需要根据指令的状态来判断是否清楚指令
            if(mPortOccupy[i].isCmdOcuupy() && mPortOccupy[i].isOutCab())
            {
                //查询是属于该货位的指令s
                auto it = std::find_if(mServerCommand.begin(), mServerCommand.end(), [ = ](InOutCabCommand_Server value) {
                    return value.PortNo == i;
                });

                if(it == mServerCommand.end())
                {
                    mylog<<QString("not find port %1 belong to any cmd,but it is cmdoccupied").arg(i);
                }
                //出柜指令执行完成并且档案被拿走
                if(it->CmdState == CmdState_Done)
                {
                    mPortOccupy[i].setCmdOccupy(false);
                    mServerCommand.removeOne(*it);
                    mCurrentInOutCmd = "";
                }
            }
        }
    }


}
/***************************************************************
* 函数名称：slot_subConfigInsertDB(QList<QVariantList> recordlist)
* 功能描述：接收来自货位配置页面的柜体节和层的信息并写入数据库
* 参数说明：recordlist-数据
* 返回值说明：
***************************************************************/
void DispatchCore::slot_subConfigInsertDB(QList<QVariantList> recordlist)
{
    //先清空以前的数据
    DeviceDBManager::getInstance()->mSubConfigDB.clearTable();
    //全部重新写入
    for(int i=0;i<recordlist.size();i++)
    {
        mylog<<DeviceDBManager::getInstance()->mSubConfigDB.insertRecord(recordlist[i]);
    }
}
/***************************************************************
* 函数名称：slot_portConfigInsertDB(QList<QVariantList> recordlist)
* 功能描述：接收来自货位配置页面的出货口信息并写入数据库
* 参数说明：recordlist-数据
* 返回值说明：
***************************************************************/
void DispatchCore::slot_portConfigInsertDB(QList<QVariantList> recordlist)
{
    //先清空以前的数据
    DeviceDBManager::getInstance()->mPortConfigDB.clearTable();
    //全部重新写入
    for(int i=0;i<recordlist.size();i++)
    {
        mylog<<DeviceDBManager::getInstance()->mPortConfigDB.insertRecord(recordlist[i]);
    }
}

void DispatchCore::slot_deviceConfigDB(QList<QVariantList> recordlist)
{
    for(int i=0;i<recordlist.size();i++)
    {
        QVariantMap condition;
        condition.insert("type",recordlist[0]);
        QVariantMap value;
        value.insert("value",recordlist[1]);
        DeviceDBManager::getInstance()->mDeviceConfigDB.updateRecord(condition,value);
    }
}
/***************************************************************
* 函数名称：slot_goodsDistributeCalculate
* 功能描述：根据层和节的定位位置生成每个货位的位置信息以及距离原点的代价距离
* 参数说明：
* 返回值说明：
***************************************************************/
void DispatchCore::slot_goodsDistributeCalculate()
{
    //先清空以前的数据
    DeviceDBManager::getInstance()->mGoodsInfoDB.clearTable();
    //总节数
    int totalsub = DeviceDBManager::getInstance()->getDeviceInfo("TotalSub").toInt();
    //总层数
    int totallayer = DeviceDBManager::getInstance()->getDeviceInfo("TotalLayer").toInt();
    //出货口所在层
    int portlayer = DeviceDBManager::getInstance()->getDeviceInfo("PortLayer").toInt();
    int id=0;
    for(int layer=1;layer<=totallayer;layer++)
    {
        for(int face=0;face<2;face++)
        {
            int cell = 1;//记录每一层第几格
            for(int sub=0;sub<=totalsub;sub++)
            {
                //跳过出货口
                if(sub == 0 && layer == portlayer)
                    continue;
                SubInfoModel subinfo;
                subinfo.sub = sub;
                subinfo.layer = layer;
                if(face == 0)
                    subinfo.face = "A";
                else
                    subinfo.face = "B";
                //根据层和节查询出这一段的货位数量和规格
                DeviceDBManager::getInstance()->getSubInfo(subinfo);
                for(int subcount=0;subcount<subinfo.count;subcount++)
                {
                    QVariantList goods;
                    goods.append(id);//id
                    goods.append(layer);//层
                    goods.append(cell);//格
                    goods.append(subinfo.face);//AB面
                    goods.append(subinfo.standard);//规格
                    goods.append(StorgeGoodsState::State_Free);//状态
                    //计算x  未加偏移
                    int x = subinfo.positionX + subinfo.standard*subcount*KincoServoManager::AxisX_1mm_INC;
                    goods.append(x);//X
                    goods.append(subinfo.positionY);//Y
                    //计算欧氏距离 先将数据缩小10000倍，否则平方的时候超出范围
                    int cost = goodsCostCalculate(qAbs(x/10000) ,qAbs(subinfo.positionY/10000));
                    //在B面，代价距离加一个50的偏置
                    if(face == 1)
                        cost += 50;
                    goods.append(cost);
                    //写入数据库
                    DeviceDBManager::getInstance()->mGoodsInfoDB.insertRecord(goods);
                    cell++;id++;
                }
            }
        }

    }
}

void DispatchCore::slot_ServerCommandAnalysis(QJsonObject data)
{
    if(data.contains("CmdType"))
    {
        QString cmdtype = data.value("CmdType").toString();

        if (cmdtype == "CONFIG") // 设备参数配置指令
        {
            // 向服务端反馈消息
            //DeviceService::GetInstance()->PostGeneralCommandResult("CONFIG", requestNo, DeviceParamsConfig::GetInstance()->GetDeviceNo(), "S", UT("已收到"));

            // 配置参数
            //ConfigDeviceParams(pcData.toMap());
        }
        else if (cmdtype == "ENTRY_CAB" || cmdtype == "OUT_CAB") //  入柜/出柜指令
        {
            // 向服务器反馈消息
            GeneralCmdResult result;
            result.cmdType = "CONTROL";
            result.deviceNo = 2;
            result.requestNo = data.value("RequestNo").toString().toStdString();
            //HttpClientManager::GetInstance()->PostGeneralCommandResult(result);

            // 配置出入柜指令
            configArvInOutCmd(cmdtype, data.value("RfidData").toVariant().toList());
        }
//        else if (cmdType == "CONTROL") //  控制指令
//        {
//            ConfigServerControl(pcData.toMap());
//        }
//        else if (cmdType == "PROBE") //  状态监测指令
//        {
//            // 汇集传感器信息和设备状态，上报服务器
//            PostDevStatusToServer(requestNo);
//        }
//        else if (cmdType == "WARNING")
//        {
//            // 向主UI发射信号
//            emit sig_ReceiveWarningInfo(pcData);
//        }
//        else if (cmdType == "CHANGEIP") //  修改注册的服务器IP地址
//        {
//            DeviceParamsConfig::GetInstance()->SetServerIP(pcData.toMap().value("IP").toString());
//        }
//        else if (cmdType == "UPDATE_TABLE_TITLE")  // 修改表头中文显示
//        {
//            // 向服务器反馈消息
//            DeviceService::GetInstance()->PostGeneralCommandResult("UPDATE_TABLE_TITLE", requestNo, DeviceParamsConfig::GetInstance()->GetDeviceNo(),
//                                                                   "S", UT("已收到"));

//            // 配置表头
//            ConfigTableTitleName(pcData.toMap().value("TableNo").toInt(), pcData.toMap().value("Title").toMap());
//        }
    }

}
/***************************************************************
* 函数名称：slot_cmdDelete(int index, QString qrcode)
* 功能描述：删除指定的指令
* 参数说明：index-指令在前端列表中的序号  qrcode-用于比对是否和前端列表的指令相同
* 返回值说明：
***************************************************************/
void DispatchCore::slot_cmdDelete(int index, QString qrcode)
{
    if(mServerCommand[index].QRCode != qrcode)
    {
        mylog<<"table and cmdlist are mismatching!! ";
        return;
    }
    //如果删除的指令是入柜并且已经分配了存储货位，那么删除该货位的时候需要把该指令占据的存储货位重新释放
    if(mServerCommand[index].CmdType == "入柜" && mServerCommand[index].LayerNo != -1)
        DeviceDBManager::getInstance()->setGoodsState(mServerCommand[index],StorgeGoodsState::State_Free);
    //如果分配了出货口，那么释放出货口位置
    if(mServerCommand[index].PortNo != -1)
        mPortOccupy[mServerCommand[index].PortNo].setCmdOccupy(false);
    mServerCommand.removeAt(index);
}
/***************************************************************
* 函数名称：slot_cmdClear()
* 功能描述：清除出入柜指令，但是如果有正在执行的指令则保留此指令等待执行完成
* 参数说明：
* 返回值说明：
***************************************************************/
void DispatchCore::slot_cmdClear()
{
    if(mServerCommand.empty())
        return;

    QMutableListIterator<InOutCabCommand_Server> it(mServerCommand);
    //遍历指令列表清空，只留下正在执行的指令
    while(it.hasNext())
    {
        InOutCabCommand_Server &serverCmd = it.next(); // 注意必须使用 next()
        if(serverCmd.CmdState != CmdState_Running)
            it.remove();
    }
    if(!mServerCommand.empty())
        emit sig_addCmdToTable(mServerCommand[0]);
    mylog<<"clear all cmds but which state is running!";
}

void DispatchCore::slot_getDevArvStatistics()
{
    DevArvStatisticsModel statistcs = HttpClientManager::GetInstance()->GetDevArvStatistics();
    if(statistcs.devNo != mDeviceNo)
    {
        //查询失败
        mylog<<"12321";
        return;
    }
    emit sig_freshChartView(statistcs);
}

void DispatchCore::slot_userInfoInsertDB(QVariantList record)
{
    //插入新的用户数据
    DeviceDBManager::getInstance()->mUserInfoDB.insertRecord(record);
    //重新获取全部的数据然后发送信号更新到UI中。在UI中直接读取数据库数据照样会程序崩溃，初始化时读取不影响。
    QList<QVariantList> allrecord;
    if(DeviceDBManager::getInstance()->mUserInfoDB.getAllRecord(allrecord))
    {
        emit sig_freshUserInfo(allrecord);
    }
}

void DispatchCore::slot_getUserInfo()
{
    QList<QVariantList> allrecord;
    if(DeviceDBManager::getInstance()->mUserInfoDB.getAllRecord(allrecord))
    {
        emit sig_freshUserInfo(allrecord);
    }
}

void DispatchCore::slot_updateUserInfo(QVariantMap condition, QVariantMap value)
{
    DeviceDBManager::getInstance()->mUserInfoDB.updateRecord(condition,value);
    //重新获取全部的数据然后发送信号更新到UI中。在UI中直接读取大量数据库数据照样会程序崩溃，初始化时读取不影响。
    QList<QVariantList> allrecord;
    if(DeviceDBManager::getInstance()->mUserInfoDB.getAllRecord(allrecord))
    {
        emit sig_freshUserInfo(allrecord);
    }
}

void DispatchCore::slot_deleteUserInfo(QVariantMap conditions)
{
    DeviceDBManager::getInstance()->mUserInfoDB.deleteRecord(conditions);
    //重新获取全部的数据然后发送信号更新到UI中。在UI中直接读取大量数据库数据照样会程序崩溃，初始化时读取不影响。
    QList<QVariantList> allrecord;
    if(DeviceDBManager::getInstance()->mUserInfoDB.getAllRecord(allrecord))
    {
        emit sig_freshUserInfo(allrecord);
    }
}

void DispatchCore::slot_sendSearchPost(QVariantMap params)
{
    try
    {
        // 调用web api从服务端查询档案信息
        DevArvSearchResponse response = HttpClientManager::GetInstance()->GetSearchArchives(params);
        // 提取所有档案信息
        PagedResultDto<DevArvData> arvs = response.result;
        // 发射查询档案信息的信号
        //emit sig_SearchInfoReceived(arvs);
    }
    catch (QString ex)
    {
        // 发射http通信错误的信号
        //emit sig_HttpErrorHappened(ex);
    }
}
/***************************************************************
* 函数名称：slot_addCmdFromPad
* 功能描述：将每一条业务指令逐一post到服务端，如果登记成功则添加到调度核的指令列表中
* 参数说明：cmdlist-SearchUI页面的业务指令
* 返回值说明：
***************************************************************/
void DispatchCore::slot_addCmdFromPad(QList<SearchCmdModel> cmdlist)
{
    //先逐个上报上位机确定登记成功后才可添加到出入柜指令列表和前端UI
    foreach(SearchCmdModel cmd,cmdlist)
    {
        //静态转换成父类ArchiveRegisterRequest用于post业务登记记录
        ArchiveRegisterRequest request = static_cast<ArchiveRegisterRequest>(cmd);
        ResponseModel model = HttpClientManager::GetInstance()->PostArchiveRegisterRequest(request);
        //如果登记成功
        if(model.respCode == "0000")
        {
            //登记成功，添加到出入柜指令中
            InOutCabCommand_Server servercmd = static_cast<InOutCabCommand_Server>(cmd);
            //查询是否有重复指令
            if(isSameInOutCabCmd(servercmd))
            {
                //指令重复跳过
                continue;
            }
            //添加进指令列表
            mServerCommand.append(servercmd);
            //发送给mainwindow显示
            sig_addCmdToTable(servercmd);
        }
        else
        {
            //登记失败
        }
    }
}

void DispatchCore::slot_newWsConnection()
{
    if (m_client) {
        mylog<<"only one clinet:"<<m_client->peerAddress()<<":"<<m_client->peerPort()<<" has connected!";
        auto socket = m_pWebSocketServer->nextPendingConnection();
        socket->close(QWebSocketProtocol::CloseCodeNormal, "Only one client allowed");
        socket->deleteLater();
        return;
    }

    m_client = m_pWebSocketServer->nextPendingConnection();
    mylog<<m_client->peerAddress()<<" ws has connected!";

    connect(m_client, SIGNAL(textMessageReceived(const QString &)), this, SLOT(slot_textMessageReceived(const QString &)));
    connect(m_client,SIGNAL(binaryMessageReceived(const QByteArray &)),this,SLOT(slot_binaryMessageReceived(const QByteArray &)));
    connect(m_client, SIGNAL(disconnected()), this, SLOT(slot_wsdisconnected()));
    pKincoServoManager-> setWebsocket(m_client);
}

void DispatchCore::slot_wsdisconnected()
{
    QWebSocket *pClientSocket = qobject_cast<QWebSocket *>(sender());
    if (pClientSocket) {
        mylog<<pClientSocket->peerAddress()<<" disconnected!";
        pClientSocket->deleteLater();

        if(pClientSocket == m_client){
            m_client->deleteLater();
            m_client = nullptr;
        }
    }
}

void DispatchCore::slot_textMessageReceived(const QString &message)
{
    QJsonParseError jsonError;
    QJsonObject json;
    json = QJsonDocument::fromJson(message.toUtf8(),&jsonError).object();
    if(json.isEmpty() == false && jsonError.error == QJsonParseError::NoError)  //  转换成功并且收到的数据非空
    {
        mylog<<"recv cmd from server:"<<json;
        recieveDataFromClient(json);
    }
    else
    {
        mylog<<jsonError.error;
    }
}

void DispatchCore::slot_binaryMessageReceived(const QByteArray &message)
{
    QImage image;
    if (image.loadFromData(message, "PNG")) {
        image = image.scaled(1000, 1000, Qt::KeepAspectRatio, Qt::SmoothTransformation);
        emit sig_imgCmdResult(image);
        // 处理图像...
    } else {
        mylog << "Failed to load image from data!";
    }
}

void DispatchCore::slot_taskTimerTimeout()
{
    //如果指令队列不为空，则开始判断为每条指令分配出货口位置
    if(!mServerCommand.empty())
    {
        //遍历指令列表，为没有分配出货口货位的指令根据出货口是否有相应规格的空位分配货位
        for(int i=0;i<mServerCommand.size();i++)
        {
            //如果命令是阻塞，则尝试分配货位
            if(mServerCommand[i].CmdState == CmdState_Blocked)
            {
                int pos;
                //入柜先分配货位
                if(mServerCommand[i].LayerNo == -1){
                    //如果分配失败说明当前该规格货位没有空位，则不参与分配出货口，直接下一条指令
                    if(!DeviceDBManager::getInstance()->distributeGoods(mServerCommand[i])){
                        mylog<<QString("distribute goods failed!cmdqrcode:%1").arg(mServerCommand[i].QRCode);
                        continue;
                    }
                }
                if(distributePort(mServerCommand[i].Standard,pos)){
                    //给指令分配指定出货口
                    mServerCommand[i].PortNo = pos;
                    //如果是出柜则可以直接为ready态
                    if(mServerCommand[i].CmdType == "出柜")
                        mServerCommand[i].CmdState = CmdState_Ready;
                    //如果是入柜，则需要等待指定货位放入档案后再变成ready态
                    else if(mServerCommand[i].CmdType == "入柜")
                        mServerCommand[i].CmdState = CmdState_Suspended;
                    //设置该货位为命令占据
                    mPortOccupy[pos].setCmdOccupy(true);
                    if(mServerCommand[i].CmdType == "出柜")
                        mPortOccupy[pos].setCmdOutCab(true);
                    else
                        mPortOccupy[pos].setCmdOutCab(false);
                }
            }
        }

        //如果当前没有执行的指令，则从列表中选择第一个状态为ready的指令执行
        if(mCurrentInOutCmd == "")
        {
            for(int i=0;i<mServerCommand.size();i++)
            {
                if(mServerCommand[i].CmdState == CmdState_Ready)
                {
                    InOutCabCommand_Board boardCmd;
                    //赋值出货口xy坐标和货位xy坐标
                    if(DeviceDBManager::getInstance()->getGoodsPosition(mServerCommand[i],boardCmd) == false){
                        //如果根据货位查询数据库坐标信息失败
                        mServerCommand[i].CmdState = CmdState_dead;    //指令变为死亡态
                        mylog<<QString("cmd qrcode:%1 search position from db failed,set state suspended!").arg(mServerCommand[i].QRCode);
                        continue;
                    }
                    mCurrentInOutCmd = mServerCommand[i].QRCode;
                    mServerCommand[i].CmdState = CmdState_Running;
                    //AB面,默认A面
                    if(mServerCommand[i].Face == "B")
                        boardCmd.Face = ARVCMDFACE::face_b;
                    //QR码
                    boardCmd.QRCode = mServerCommand[i].QRCode;
                    //是否有下条指令，默认无
                    if(mServerCommand.size()>1)
                        boardCmd.HaveNext = true;
                    //出入柜,默认入柜，存
                    if(mServerCommand[i].CmdType == "OUT_CAB")
                        boardCmd.Type = ARVCMDTYPE::type_outcab;

                    mylog<<QString("select the qrcode:%1 to run").arg(mCurrentInOutCmd);
                    //下发指令给Linux控制板
                    slot_inOutCabCmd(boardCmd);
                    break;
                }
            }
        }
    }
    //通知mainwindow更新门货位信息和指令列表信息
    emit sig_freshPortInfo(mPortOccupy,mServerCommand);
}
/***************************************************************
* 函数名称：QRLocateResultProcess(QRLocateCommandResponse response)
* 功能描述：根据Linux控制板报上来的每一段的定位结果来解析处理
* 参数说明：response 定位结果model
* 返回值说明：无
***************************************************************/
void DispatchCore::QRLocateResultProcess(QRLocateCommandResponse response)
{
    //如果定位成功
    if(response.LocateResult)
    {
        //定位节
        if(response.LocateType == "QRLOCATE_X")
        {
            QVariantMap condition;
            condition.insert("sub",response.LocateCnt);
            QVariantMap value;
            value.insert("positionX",response.LocatePosition);
            //将该节的X全部修改
            DeviceDBManager::getInstance()->mSubConfigDB.updateRecord(condition,value);
        }
        //定位层
        else if(response.LocateType == "QRLOCATE_Y")
        {
            QVariantMap condition;
            condition.insert("layer",response.LocateCnt);
            QVariantMap value;
            value.insert("positionY",response.LocatePosition);
            //将该层的Y全部修改
            DeviceDBManager::getInstance()->mSubConfigDB.updateRecord(condition,value);

        }
        //定位完成
        else if(response.LocateType == "QRLOCATE_DONE")
        {
            //发送信号刷新表格显示
            emit sig_storgeUIRefreshTable();
        }
    }
    else
    {
        mylog<<QString("qrlocate failed! Type:%1,Cnt:%2").arg(QString::fromStdString(response.LocateType))
               .arg(response.LocateCnt);
    }
}

void DispatchCore::InOutCabCmdResultProcess(InOutCabCommandResponse response)
{
    //根据qr码查找该指令所对应的指令列表中的指令
    auto it = std::find_if(mServerCommand.begin(), mServerCommand.end(), [ = ](InOutCabCommand_Server value) {
        return value.QRCode.toStdString() == response.QRCode;
    });
    if(it == mServerCommand.end())
        return;
    //如果指令操作成功并且是入柜，那么可以直接删除指令。出柜操作要等待档案从出货口拿出后才算完成，在slot_portMessageProcess函数中处理
    if(response.Result)
    {
        it->CmdState = CmdState_Done;
        if(it->CmdType == "入柜")
        {
            //清除该指令所占的出货口
            mPortOccupy[it->PortNo].setCmdOccupy(false);
            mServerCommand.removeOne(*it);
            //清空当前正在执行的指令
            mCurrentInOutCmd = "";
        }
    }
    //如果操作失败，将该指令的状态变为dead，并打印失败的详细原因
    else
    {
        //清除该指令所占的出货口
        mPortOccupy[it->PortNo].setCmdOccupy(false);
        it->CmdState = CmdState_dead;
        mylog<<QString::fromStdString(response.ErrMsg);
        //清空当前正在执行的指令
        mCurrentInOutCmd = "";
    }
}
/***************************************************************
* 函数名称：goodsCostCalculate(int x, int y)
* 功能描述：计算欧氏距离
* 参数说明：
* 返回值说明：
***************************************************************/
int DispatchCore::goodsCostCalculate(int x, int y)
{
    if(x == 0)
        return y;
    else if(y == 0)
        return  x;
    else
    {
        return qSqrt(x*x+y*y);
    }
}

void DispatchCore::configArvInOutCmd(QString cmdType, QList<QVariant> rfidData)
{
    if (rfidData.count() == 0) return;
    foreach (QVariant data, rfidData)
    {
        int dstLayerNo = data.toMap().value("LayerNo").toInt(); // 目标层信息
        //  本层相关档案和标签明细
        QList<QVariant> arvs = data.toMap().value("Labels").toList();
        foreach (QVariant arv, arvs)
        {
            InOutCabCommand_Server arvInfo;
            //入柜不使用上位机下发的位置信息，屏重新分配,如果修改此部分逻辑，记得修改查询UI中主动发起借阅业务时指令的组装部分函数
            if (cmdType == "ENTRY_CAB") arvInfo.CmdType = "入柜";
            //出柜使用上位机下发的位置信息
            else if (cmdType == "OUT_CAB")
            {
                arvInfo.CmdType = "出柜";
                arvInfo.CellNo = arv.toMap().value("CellNo").toInt();
                arvInfo.LayerNo = dstLayerNo;
                arvInfo.Face = "A";//arv.toMap().value("Face").toString();
            }
            else arvInfo.CmdType = "异常";

            arvInfo.Name   = arv.toMap().value("Name").toString();
            arvInfo.CardID = arv.toMap().value("CardID").toString();
            arvInfo.QRCode = arv.toMap().value("RfidLabel").toString();
            arvInfo.Standard = 40;//arv.toMap().value("Standard").toInt();

            //查询是否有重复指令
            if(isSameInOutCabCmd(arvInfo))
            {
                //指令重复跳过
                continue;
            }

            //添加进指令列表
            mServerCommand.append(arvInfo);
            //发送给mainwindow显示
            sig_addCmdToTable(arvInfo);
        }
    }
}
/***************************************************************
* 函数名称：isSameInOutCabCmd
* 功能描述：判断cmd与当前的命令列表中是否有重复的指令
* 参数说明：cmd-出入柜指令
* 返回值说明：true-重复指令  false-不重复
***************************************************************/
bool DispatchCore::isSameInOutCabCmd(InOutCabCommand_Server cmd)
{
    //查询是否有重复指令
    auto it = std::find_if(mServerCommand.begin(), mServerCommand.end(), [ = ](InOutCabCommand_Server value) {
        return value.QRCode == cmd.QRCode;
    }); // 查找list中qrcode是否有重复的

    if(it != mServerCommand.end())
    {
        //指令重复跳过
        return true;
    }
    else
        return false;
}
